/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<asm.h>
#include	<map.h>
#include	<sig.h>
#include	<page.h>
#include	<file.h>
#include	<param.h>
#include	<errno.h>
#include	<inode.h>
#include	<string.h>

pid_t mpid;
uint highpri;
struct proc *up;
struct proc proc[NPROC];

int
proc_init(void)
{
	int i;

	mpid = 0;
	highpri = 0;
	memset(proc, 0, sizeof(struct proc));
	up = proc;
	up->p_pri = PUSER;
	up->p_stat = SRUN;
	up->p_flag = SLOAD | SSYS;
	up->p_pgdir = kpgdir;
	for(i = 0; i < NSIG; i++) 
		up->p_signal[i] = SIG_IGN;
	if(!(up->p_stack = malloc0(kernmap, 1))){
		up->p_error = ENOMEM;
		return -1;
	}
	return 0;
}

int
copy_proc_space(uint *sp, uint *dp)
{
	uint i, j, *p, *q;

	for(i = 0; i < PG_KERN_NUM; i++)
		dp[i] = sp[i];
        for(i = PG_KERN_NUM; i < PG_TBL_NUM; i++){
		if(sp[i]){ 
			if(!(p = malloc(kernmap, 1)))
				goto out;
			dp[i] = (uint)p | PG_P | PG_W | PG_U;
			q = (uint *)(sp[i] & PG_ADDR_MASK);
			for(j = 0; j < PG_TBL_NUM; j++){
				if(q[j]){
					p[j] = q[j] = (q[j] | PG_COW) & ~(PG_W);
					page_list_alloc(PAGE_LIST_ADDR(q[j]));
				}else
					p[j] = 0;
			}
		}else
			dp[i] = 0;
        }
	flush_tbl();
	return 0;
out:
	while(--i >= PG_KERN_NUM){ 
		mfree(kernmap, 1, dp[i] & PG_ADDR_MASK);
		p = (uint *)(sp[i] & PG_ADDR_MASK);
		for(j = 0; j < PG_TBL_NUM; j++){
			if(p[j]){
				p[j] = (p[j] | PG_W) & ~(PG_COW);
				page_list_free(p[j] & PG_SWP ? (p[j] & PG_ADDR_MASK) >> PG_PTE_SHIFT : p[j] & PG_ADDR_MASK);	
			}
		}
	}
	return -1;
}

struct proc *
newproc(void)
{
	uint i;
	struct inode *dp;
	struct proc *p, *p1;

	mpid++;
	p = NULL;
	for(p1 = &proc[0]; p1 < &proc[NPROC]; p1++){
		if(p1->p_pid == mpid){
			mpid++;
			p1 = proc;
			continue;
		}
		if(p1->p_stat == SFREE){
			p = p1;
			break;
		}
	}
	if(p == NULL){
		up->p_error = EAGAIN;
		return NULL;
	}
	memmove(p, up, sizeof(struct proc));
	p->p_stat = SIDL;
	if(!(p->p_pgdir = malloc0(kernmap, 1))){
		p->p_stat = SFREE;
		up->p_error = ENOMEM;
		return NULL;
	}
	if(!(p->p_stack = malloc0(kernmap, 1))){
		mfree(kernmap, 1, (uint)p->p_pgdir);
		p->p_stat = SFREE;
		up->p_error = ENOMEM;
		return NULL;
	}
	p1 = up;
	if(copy_proc_space(p1->p_pgdir, p->p_pgdir) == -1){
		mfree(kernmap, 1, (uint)p->p_pgdir);
		mfree(kernmap, 1, (uint)p->p_stack);
		p->p_stat = SFREE;
		up->p_error = ENOMEM;
		return NULL;
	}
	for(i = 0; i < NOFILE; i++){
                if(p->p_ofile[i])
			((struct file *)p->p_ofile[i])->f_count++;
        }
	if(dp = p->p_cdir)	
		dp->i_count++;
	if(dp = p->p_rdir)
		dp->i_count++;
	p->p_pid = mpid;
	p->p_pri = PUSER;
	p->p_flag = SLOAD;
	p->p_wchan = NULL;
	p->p_ppid = p1->p_pid;
	p->p_sig = p->p_cpu = p->p_time = p->p_error = p->p_exit = p->p_lesp = 0;
	return p;
}

void
swtch(void)
{
	struct proc *p, *rp;	

	p = NULL;
        for(rp = &proc[0]; rp < &proc[NPROC]; rp++){
                if(rp->p_stat == SREADY && !(p->p_flag & SLOCK)){
                        if(p == NULL || rp->p_pri < p->p_pri || (rp->p_pri == p->p_pri && rp->p_cpu < p->p_cpu))
                                p = rp;
                }
        }
loop:
        if(p && p != up){
		highpri = 0;
		memmove(up->p_stack, (uint *)kstk, PAGESIZ);
               	asm volatile("movl %%esp, %0" : "=a"(up->p_esp));
		if(save((uint)up->p_stack + up->p_esp - kstk)){
			up->p_esp -= NCONTEXT;
			if(up->p_stat == SRUN)
				up->p_stat = SREADY;
			up = p;
			lcr3(up->p_pgdir);
			up->p_stat = SRUN;
			regain(kstk, up->p_stack, up->p_esp);
		}else
			return;
        }
	if(up->p_stat == SZOMB || up->p_stat == SSLEEP || up->p_stat == SWAIT){
		p = proc;
		goto loop;
	}
}

void
setpri(struct proc *p)
{
	p->p_pri = p->p_cpu / 2 + PUSER + p->p_nice;
	if(p->p_pri > PIDLE)
		p->p_pri = PIDLE;
	if(up != p && p->p_pri < up->p_pri)
		highpri++;
}

void
sched(void)
{
	for( ; ; )
		swtch();
}
